{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Unit commitment\n",
    "\n",
    "\n",
    "This tutorial runs through examples of unit commitment for generators at a single bus. Examples of minimum part-load, minimum up time, minimum down time, start up costs, shut down costs and ramp rate restrictions are shown.\n",
    "\n",
    "To enable unit commitment on a generator, set its attribute committable = True."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pypsa\n",
    "import pandas as pd"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Minimum part load demonstration\n",
    "\n",
    "In final hour load goes below part-load limit of coal gen (30%), forcing gas to commit."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nu = pypsa.Network(snapshots=range(4))\n",
    "\n",
    "nu.add(\"Bus\",\"bus\")\n",
    "\n",
    "nu.add(\"Generator\",\"coal\",bus=\"bus\",\n",
    "       committable=True,\n",
    "       p_min_pu=0.3,\n",
    "       marginal_cost=20,\n",
    "       p_nom=10000)\n",
    "\n",
    "nu.add(\"Generator\",\"gas\",bus=\"bus\",\n",
    "       committable=True,\n",
    "       marginal_cost=70,\n",
    "       p_min_pu=0.1,\n",
    "       p_nom=1000)\n",
    "\n",
    "nu.add(\"Load\",\"load\",bus=\"bus\",p_set=[4000,6000,5000,800])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nu.lopf()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nu.generators_t.status"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nu.generators_t.p"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Minimum up time demonstration\n",
    "\n",
    "Gas has minimum up time, forcing it to be online longer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nu = pypsa.Network(snapshots=range(4))\n",
    "\n",
    "nu.add(\"Bus\",\"bus\")\n",
    "\n",
    "nu.add(\"Generator\",\"coal\",bus=\"bus\",\n",
    "       committable=True,\n",
    "       p_min_pu=0.3,\n",
    "       marginal_cost=20,\n",
    "       p_nom=10000)\n",
    "\n",
    "nu.add(\"Generator\",\"gas\",bus=\"bus\",\n",
    "       committable=True,\n",
    "       marginal_cost=70,\n",
    "       p_min_pu=0.1,\n",
    "       up_time_before=0,\n",
    "       min_up_time=3,\n",
    "       p_nom=1000)\n",
    "\n",
    "nu.add(\"Load\",\"load\",bus=\"bus\",p_set=[4000,800,5000,3000])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nu.lopf()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nu.generators_t.status"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nu.generators_t.p"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Minimum down time demonstration\n",
    "\n",
    "Coal has a minimum down time, forcing it to go off longer."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nu = pypsa.Network(snapshots = range(4))\n",
    "\n",
    "nu.add(\"Bus\",\"bus\")\n",
    "\n",
    "nu.add(\"Generator\",\"coal\",bus=\"bus\",\n",
    "       committable=True,\n",
    "       p_min_pu=0.3,\n",
    "       marginal_cost=20,\n",
    "       min_down_time=2,\n",
    "       down_time_before=1,\n",
    "       p_nom=10000)\n",
    "\n",
    "nu.add(\"Generator\",\"gas\",bus=\"bus\",\n",
    "       committable=True,\n",
    "       marginal_cost=70,\n",
    "       p_min_pu=0.1,\n",
    "       p_nom=4000)\n",
    "\n",
    "nu.add(\"Load\",\"load\",bus=\"bus\",p_set=[3000,800,3000,8000])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nu.lopf()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nu.objective"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nu.generators_t.status"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nu.generators_t.p"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Start up and shut down costs\n",
    "\n",
    "Now there are associated costs for shutting down, etc"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nu = pypsa.Network(snapshots=range(4))\n",
    "\n",
    "nu.add(\"Bus\",\"bus\")\n",
    "\n",
    "nu.add(\"Generator\",\"coal\",bus=\"bus\",\n",
    "       committable=True,\n",
    "       p_min_pu=0.3,\n",
    "       marginal_cost=20,\n",
    "       min_down_time=2,\n",
    "       start_up_cost=5000,\n",
    "       p_nom=10000)\n",
    "\n",
    "nu.add(\"Generator\",\"gas\",bus=\"bus\",\n",
    "       committable=True,\n",
    "       marginal_cost=70,\n",
    "       p_min_pu=0.1,\n",
    "       shut_down_cost=25,\n",
    "       p_nom=4000)\n",
    "\n",
    "nu.add(\"Load\",\"load\",bus=\"bus\",p_set=[3000,800,3000,8000])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nu.lopf(nu.snapshots)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nu.objective"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nu.generators_t.status"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nu.generators_t.p"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ramp rate limits"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nu = pypsa.Network(snapshots=range(6))\n",
    "\n",
    "nu.add(\"Bus\",\"bus\")\n",
    "\n",
    "nu.add(\"Generator\",\"coal\",bus=\"bus\",\n",
    "       marginal_cost=20,\n",
    "       ramp_limit_up=0.1,\n",
    "       ramp_limit_down=0.2,\n",
    "       p_nom=10000)\n",
    "\n",
    "nu.add(\"Generator\",\"gas\",bus=\"bus\",\n",
    "       marginal_cost=70,\n",
    "       p_nom=4000)\n",
    "\n",
    "nu.add(\"Load\",\"load\",bus=\"bus\",p_set=[4000,7000,7000,7000,7000,3000])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nu.lopf()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nu.generators_t.p"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nu = pypsa.Network(snapshots=range(6))\n",
    "\n",
    "nu.add(\"Bus\",\"bus\")\n",
    "\n",
    "nu.add(\"Generator\",\"coal\",bus=\"bus\",\n",
    "       marginal_cost=20,\n",
    "       ramp_limit_up=0.1,\n",
    "       ramp_limit_down=0.2,\n",
    "       p_nom_extendable=True,\n",
    "       capital_cost=1e2)\n",
    "\n",
    "nu.add(\"Generator\",\"gas\",bus=\"bus\",\n",
    "       marginal_cost=70,\n",
    "       p_nom=4000)\n",
    "\n",
    "nu.add(\"Load\",\"load\",bus=\"bus\",\n",
    "       p_set=[4000,7000,7000,7000,7000,3000])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nu.lopf(nu.snapshots)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nu.generators.p_nom_opt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nu.generators_t.p"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nu = pypsa.Network(snapshots=range(7))\n",
    "\n",
    "nu.add(\"Bus\",\"bus\")\n",
    "\n",
    "#Can get bad interactions if SU > RU and p_min_pu; similarly if SD > RD\n",
    "nu.add(\"Generator\",\"coal\",bus=\"bus\",\n",
    "       marginal_cost=20,\n",
    "       committable=True,\n",
    "       p_min_pu=0.05,\n",
    "       initial_status=0,\n",
    "       ramp_limit_start_up=0.1,\n",
    "       ramp_limit_up=0.2,\n",
    "       ramp_limit_down=0.25,\n",
    "       ramp_limit_shut_down=0.15,\n",
    "       p_nom=10000.)\n",
    "\n",
    "nu.add(\"Generator\",\"gas\",bus=\"bus\",\n",
    "       marginal_cost=70,\n",
    "       p_nom=10000)\n",
    "\n",
    "nu.add(\"Load\",\"load\",bus=\"bus\",\n",
    "       p_set=[0.,200.,7000,7000,7000,2000,0])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nu.lopf()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nu.generators_t.p"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nu.generators_t.status"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nu.generators.loc[\"coal\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Rolling horizon example\n",
    "\n",
    "This example solves sequentially in batches"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sets_of_snapshots = 6\n",
    "p_set = [4000,5000,700,800,4000]\n",
    "\n",
    "nu = pypsa.Network(snapshots=range(len(p_set)*sets_of_snapshots))\n",
    "\n",
    "nu.add(\"Bus\",\"bus\")\n",
    "\n",
    "nu.add(\"Generator\",\"coal\",bus=\"bus\",\n",
    "       committable=True,\n",
    "       p_min_pu=0.3,\n",
    "       marginal_cost=20,\n",
    "       min_down_time=2,\n",
    "       min_up_time=3,\n",
    "       up_time_before=1,\n",
    "       ramp_limit_up=1,\n",
    "       ramp_limit_down=1,\n",
    "       ramp_limit_start_up=1,\n",
    "       ramp_limit_shut_down=1,\n",
    "       shut_down_cost=150,\n",
    "       start_up_cost=200,\n",
    "       p_nom=10000)\n",
    "\n",
    "nu.add(\"Generator\",\"gas\",bus=\"bus\",\n",
    "       committable=True,\n",
    "       marginal_cost=70,\n",
    "       p_min_pu=0.1,\n",
    "       up_time_before=2,\n",
    "       min_up_time=3,\n",
    "       shut_down_cost=20,\n",
    "       start_up_cost=50,\n",
    "       p_nom=1000)\n",
    "\n",
    "nu.add(\"Load\",\"load\",bus=\"bus\",p_set=p_set*sets_of_snapshots)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "overlap = 2\n",
    "for i in range(sets_of_snapshots):\n",
    "    nu.lopf(nu.snapshots[i*len(p_set):(i+1)*len(p_set)+overlap], pyomo=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pd.concat({'Active':nu.generators_t.status.astype(bool), 'Output': nu.generators_t.p}, axis=1)"
   ]
  }
 ],
 "metadata": {
  "@webio": {
   "lastCommId": null,
   "lastKernelId": null
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
